﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Diagnostics;
using System.IO;

namespace BankService
{
    [Serializable]
    public class BankServiceImplementation : IBankContract, IDisposable
    {
        private static readonly object ReadWriteLock = new object();
        private static Account accountData = null;
        private const string DataFileName = @"/DataCollections.data";

        public BankServiceImplementation()
        {
            lock (ReadWriteLock)
            {   
                if (accountData == null)
                {   
                    accountData = StreamUtil.Deserialize<Account>(
                        Path.GetFullPath(Process.GetCurrentProcess().MainModule.ModuleName) + DataFileName);

                    if (accountData != null)
                    {
                        LogMessage("LoadData", accountData.AccountId, true);
                    }
                }

                if (accountData == null)
                {
                    accountData = new Account("TestAccount");
                    LogMessage("LoadData", accountData.AccountId, true);
                }
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            lock (ReadWriteLock)
            {
                StreamUtil.Serialize(accountData,
                    Path.GetFullPath(Process.GetCurrentProcess().MainModule.ModuleName) + DataFileName);
            }
            LogMessage("SaveData", accountData.AccountId, true);
        }

        #endregion

        #region BankServiceContract Members

        public bool IsAvailable()
        {
            return accountData != null;
        }

        public bool SetCanBePending(bool value)
        {
            accountData.AllowPending = value;
            return value;
        }

        public decimal GetBalance(string accountId)
        {
            lock(ReadWriteLock)
            {
                LogMessage("GetBalance", accountId, true);
                return accountData.AccountBalance;
            }
        }

        public decimal GetPendingAmount(string accountId)
        {
            lock (ReadWriteLock)
            {
                LogMessage("GetPending", accountId, true);
                return accountData.PendingAmount;
            }
        }

        public bool Withdraw(string accountId, decimal amount)
        {
            lock (ReadWriteLock)
            {
                accountData.AccountBalance -= amount;
                LogMessage("Withdraw", accountId, accountData.AccountBalance > 0M);
                return accountData.AccountBalance > 0M;
            }
             
        }

        public void Deposit(string accountId, decimal amount)
        {
            lock (ReadWriteLock)
            {
                accountData.AccountBalance += amount;
                LogMessage("Deposit", accountId, true);
            }
        }

        public void Refund(string accountId, decimal amount)
        {
            lock (ReadWriteLock)
            {
                accountData.AccountBalance += amount;
                LogMessage("Refund", accountId, true);
            }
        }


        public bool Pending(string accountId, decimal amount)
        {
            lock(ReadWriteLock)
            {
                if (accountData.AllowPending ||
                    accountData.PendingAmount + amount < accountData.AccountBalance)
                {
                    accountData.PendingAmount += amount;

                    LogMessage("Pending", accountId, true);
                    return true;
                }
                else
                {
                    LogMessage("Pending", accountId, false);
                    return false;
                }
            }
        }

        public void CancelPending(string accountId, decimal amount)
        {
            lock (ReadWriteLock)
            {
                accountData.PendingAmount -= amount;
            }

            LogMessage("CancelPending", accountId, true);
        }

        public void CommitPending(string accountId, decimal amount)
        {
            lock (ReadWriteLock)
            {
                accountData.PendingAmount -= amount;

                accountData.AccountBalance -= amount;
            }

            LogMessage("CommitPending", accountId, true);
        }

        public void Reset(string accountId)
        {
            lock (ReadWriteLock)
            {
                accountData.AccountBalance = 0.0M;

                accountData.PendingAmount = 0.0M;
            }
            Dispose();
        }

        #endregion

        private void LogMessage(string transactionType, string accountId, bool result)
        {
            Console.WriteLine(
                string.Format(
                    "{0} -- {1} | Account:{2} | Balance:{3:F2} | Pending:{4:F2}| Result:{5}",
                    transactionType, 
                    DateTime.Now.ToString("u"),
                    accountId, 
                    accountData.AccountBalance,
                    accountData.PendingAmount,
                    result ? "Succeed" : "Failed"
                    ));
        }



    }
}
